/** @file    MessageFilter.cpp
 *  @author  Alessandro Polo
 *  @version $Id: MessageFilter.cpp 2792 2010-07-23 12:19:29Z alex $
 *  @brief
 * File containing methods for the wosh::MessageFilter class.
 * The header for this class can be found in MessageFilter.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/MessageFilter.h>

 #include <core/ObjectAllocator.h>
 #include <core/BusConnector.h>
 #include <core/Bundle.h>
 #include <core/SecurityToken.h>
 #include <core/Property.h>
 #include <core/PropertiesProvider.h>
 #include <core/InterfacesProvider.h>
 #include <core/WoshKernel.h>
 #include <core/SecurityManager.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::MessageFilter, "wosh::Message", 1.01, _static_MessageFilter)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int MessageFilter::validateMessageHeader( const Message& message ) const {
	int errorSum = MessageFilter::HEADER_CMP_UNKNOWN;
	if ( this->sourceBus.size() && this->sourceBus != _Bus_ANY ) {
		if ( this->sourceBus != message.getSourceBus() )
			errorSum += MessageFilter::HEADER_CMP_ERR_BUS_SOURCE;
	 }
	if ( this->currentBus.size() && this->currentBus != _Bus_ANY ) {
		if ( this->currentBus != message.getCurrentBus() )
			errorSum += MessageFilter::HEADER_CMP_ERR_BUS_CURRENT;
	 }
	if ( this->destinationBus.size() && this->destinationBus != _Bus_ANY ) {
		if ( this->destinationBus != message.getDestinationBus() )
			errorSum += MessageFilter::HEADER_CMP_ERR_BUS_DESTINATION;
	 }
	if ( !this->source.isEmpty() ) {
		if ( !URI::pregMatch(this->source, message.getSource()) )
			errorSum += MessageFilter::HEADER_CMP_ERR_SOURCE;
	 }
	if ( !this->destination.isEmpty() ) {
		if ( !URI::pregMatch(this->destination, message.getDestination()) )
			errorSum += MessageFilter::HEADER_CMP_ERR_DESTINATION;
	 }
	if ( errorSum == MessageFilter::HEADER_CMP_UNKNOWN )
		errorSum = MessageFilter::HEADER_CMP_VALID;
	return errorSum;
 }

const char* MessageFilter::getMessageHeaderFilterResultAsString( HEADER_FILTER_RESULT value_enum ) {
	switch(value_enum) {
		case HEADER_CMP_VALID:					return "CMP_VALID";
		case HEADER_CMP_ERR_SOURCE:				return "CMP_ERR_SOURCE";
		case HEADER_CMP_ERR_DESTINATION:		return "CMP_ERR_DESTINATION";
		case HEADER_CMP_ERR_BUS_SOURCE:			return "CMP_ERR_BUS_SOURCE";
		case HEADER_CMP_ERR_BUS_CURRENT:		return "CMP_ERR_BUS_CURRENT";
		case HEADER_CMP_ERR_BUS_DESTINATION:	return "CMP_ERR_BUS_DESTINATION";
		case HEADER_CMP_UNKNOWN:
		default:								return "CMP_UNKNOWN";
	 }
 }

std::string MessageFilter::getMessageHeaderFilterResultsAsString( int value_mask ) {
	std::string res;
	if ( (value_mask & HEADER_CMP_VALID) == HEADER_CMP_VALID )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_VALID) + std::string(";");
	if ( (value_mask & HEADER_CMP_ERR_SOURCE) == HEADER_CMP_ERR_SOURCE )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_ERR_SOURCE) + std::string(";");
	if ( (value_mask & HEADER_CMP_ERR_DESTINATION) == HEADER_CMP_ERR_DESTINATION )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_ERR_DESTINATION) + std::string(";");
	if ( (value_mask & HEADER_CMP_ERR_BUS_SOURCE) == HEADER_CMP_ERR_BUS_SOURCE )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_ERR_BUS_SOURCE) + std::string(";");
	if ( (value_mask & HEADER_CMP_ERR_BUS_CURRENT) == HEADER_CMP_ERR_BUS_CURRENT )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_ERR_BUS_CURRENT) + std::string(";");
	if ( (value_mask & HEADER_CMP_ERR_BUS_DESTINATION) == HEADER_CMP_ERR_BUS_DESTINATION )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_ERR_BUS_DESTINATION) + std::string(";");

	if ( res.empty() )
		res += getMessageHeaderFilterResultAsString(HEADER_CMP_UNKNOWN);
	return res;
 }

int MessageFilter::validateMessageContent( const Message& message, int* comparison_result, int options ) const {
	if ( this->content == NULL ) {
		if ( message.getContent() == NULL )
			return MessageFilter::CONTENT_CMP_EQUAL;
		if ( (options & CONTENT_OPT_EMPTYPATTERN) == CONTENT_OPT_EMPTYPATTERN )
			return MessageFilter::CONTENT_CMP_VALID;
		return MessageFilter::CONTENT_CMP_ERR_DIFFERENT;
	 }
	if ( message.getContent() == NULL )
		return MessageFilter::CONTENT_CMP_ERR_DIFFERENT;
	if ( this->content == message.getContent() ) // same pointer!
		return MessageFilter::CONTENT_CMP_EQUAL;

	if ( (options & CONTENT_OPT_STRICTCLASSCHECK) == CONTENT_OPT_STRICTCLASSCHECK ) {
		if ( strcmp(this->content->getClassName(), message.getContent()->getClassName()) != 0 )
			return MessageFilter::CONTENT_CMP_ERR_CLASS;
	 }
	int cmp_res = this->content->compare(message.getContent());
	if ( comparison_result != NULL )
		*comparison_result = cmp_res;

	switch(cmp_res) {
		case WOSH_COMPARE_RET_IDEMPOTENT:	return MessageFilter::CONTENT_CMP_VALID;
		default:							return MessageFilter::CONTENT_CMP_ERR_DIFFERENT;
	 }
 }

const char* MessageFilter::getMessageContentFilterResultAsString( CONTENT_FILTER_RESULT value_enum ) {
	switch(value_enum) {
		case CONTENT_CMP_VALID:				return "CMP_VALID";
		case CONTENT_CMP_EQUAL:				return "CMP_EQUAL";
		case CONTENT_CMP_ERR_DIFFERENT:		return "CMP_ERR_DIFFERENT";
		case CONTENT_CMP_ERR_CLASS:			return "CMP_ERR_CLASS";
		case CONTENT_CMP_UNKNOWN:
		default:							return "CMP_UNKNOWN";
	 }
 }

std::string MessageFilter::getMessageContentFilterResultsAsString( int value_mask ) {
	std::string res;
	if ( (value_mask & CONTENT_CMP_VALID) == CONTENT_CMP_VALID )
		res += getMessageContentFilterResultAsString(CONTENT_CMP_VALID) + std::string(";");
	if ( (value_mask & CONTENT_CMP_EQUAL) == CONTENT_CMP_EQUAL )
		res += getMessageContentFilterResultAsString(CONTENT_CMP_EQUAL) + std::string(";");
	if ( (value_mask & CONTENT_CMP_ERR_DIFFERENT) == CONTENT_CMP_ERR_DIFFERENT )
		res += getMessageContentFilterResultAsString(CONTENT_CMP_ERR_DIFFERENT) + std::string(";");
	if ( (value_mask & CONTENT_CMP_ERR_CLASS) == CONTENT_CMP_ERR_CLASS )
		res += getMessageContentFilterResultAsString(CONTENT_CMP_ERR_CLASS) + std::string(";");

	if ( res.empty() )
		res += getMessageContentFilterResultAsString(CONTENT_CMP_UNKNOWN);
	return res;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool MessageFilter::isMessageTarget( const Message& message, const Object* object ) {
	if ( object == NULL ) return false;
	bool valid = URI::pregMatch( message.getDestination(), object->getURI() );
	if ( !valid ) return false;
	if ( !message.getDestination().hasExpression() )
		return true;
	// i'm not able to evaluate expressions!
 	return false;
 }

bool MessageFilter::isMessageTarget( const Message& message, const URI& itemURI, const PropertiesProvider* properties, const InterfacesProvider* interfaces )
 {
	bool uriOk = URI::pregMatch( message.getDestination(), itemURI );
	if ( !uriOk ) return false;
	if ( !message.getDestination().hasExpression() )
		return true;
 	if ( properties == NULL )
		return true;
	return validateExpression( message.getDestination().getExpression(), properties, interfaces, message.getSecurityToken() );
 }

bool MessageFilter::isMessageTarget( const Message& message, const Bundle* bundle ) {
	if ( bundle == NULL ) return false;
	return isMessageTarget( message, bundle->getURI(), bundle->bundleProperties(), bundle->bundleInterfaces() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool MessageFilter::validateExpression( const UriExpression& expression, const PropertiesProvider* properties, const InterfacesProvider* interfaces, const SecurityToken* security )
 {
	if ( properties == NULL ) return false;

	MutexLockerRead mL(properties->getMutex());
	// lock invalidating operations (REMOVAL; List::Permission updates)

	std::vector<UriPredicate>::const_iterator it;
	for ( it=expression.getPredicates().begin(); it!=expression.getPredicates().end(); ++it ) {
		const PropertyHolder* propHolder = properties->find( (*it).field );
		if ( propHolder == NULL ) {
			return false;
		 }
		MutexLockerRead mL(&propHolder->mux);
		if ( propHolder->data == NULL ) {
			return false;
		 }
		bool validated = SecurityManager::evalAuthorization(security, &propHolder->data->getPermission(), Permission::Read);
		if ( !validated ) {
			return false;
		 }
		// go on with the atomic operation, check if listener allows reading
		bool accepted = properties->reading( propHolder );
		if ( !accepted ) {
			return false;
		 }

		if ( (*it).comparison == '=' ) {
			if ( propHolder->data->getValue().toString() == (*it).value ) {
				continue;
			 }
		 }
		else if ( (*it).comparison == '~' ) {
			if ( propHolder->data->getValue().toString().find( (*it).value ) != string::npos ) {
				continue;
			 }
		 }
		return false;
	 }
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

short MessageFilter::filterReplica( const Message& message, const BusConnector* mainBus )//, const BusConnector* bus1, const BusConnector* bus2, const BusConnector* bus3, const BusConnector* bus4 )
 {
	// BusManager manages routing. Messages are routed directly from SOURCE bus to DESTINATION bus.
	// but when a IBusListener is listening two or more busses, ANYCAST messages will be received multiple times.
	// we assume all bus working correctly, so we accept the original message only, checking source Bus 
	// if destination bus is defined, then the BusManager will route it once!
	if ( message.getDestinationBus() != _Bus_ANY )
		return true;
	// we may be in following cases:
	// - BusCore => * and i'm listening on { BusCore, BusDevice, .. }
	// - BusXXXX => * and i'm listening on { BusCore, BusDevice, .. }
	// as general rule, we should keep the message within the same bus, so:
	if ( message.getSourceBus() == message.getCurrentBus() )
		return true;
	// but if the source bus is different (example: BusGui on another host)
	// we need to accept at least one of the replica, since they will be fired
	// on each (shared..) bus we are listening. so check it against mainBus
	if ( mainBus != NULL ) {
		if ( mainBus->getBusName() == message.getCurrentBus() )
			return true;
		else
			return false;
	 }
	// better to receive a message twice than not at all!
	return 2;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
